package cn.zwx.common.logger.advisor;

import cn.zwx.common.logger.config.AspectLogProperties;
import lombok.NonNull;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Controller;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zhangwenxue
 */
public class BuildPointcut {

    private final AspectLogProperties properties;

    public BuildPointcut(AspectLogProperties properties) {
        this.properties = properties;
    }
    public Pointcut buildPointcut() {
        List<Pointcut> cuts = new ArrayList<>();

        // 匹配内定义的方法匹配器, 用于支持网鱼内定义的接口注入
        MethodMatcher wyClassMatcher = new MethodMatcher() {
            @Override
            public boolean matches(@NonNull Method method, @NonNull Class<?> targetClass) {
                return inWyDeclare(method);
            }
            @Override
            public boolean isRuntime() {
                return false;
            }

            @Override
            public boolean matches(@NonNull Method method, @NonNull Class<?> targetClass, Object... args) {
                return matches(method, targetClass);
            }
        };

        // 1. 开启trace拦截
        if (properties.isTrack()) {
            cuts.add(createPointcut(ClassFilter.TRUE, wyClassMatcher));
        }

        // 2. 开启Http请求拦截
        if (properties.isHttp()) {
            Pointcut httpCut = createPointcut(clz -> AnnotationUtils.findAnnotation(clz, Controller.class) != null, wyClassMatcher);
            cuts.add(new ComposablePointcut(httpCut));
        }

        // 3. 如果一个都没开, 则不作AOP
        if (cuts.isEmpty()) {
            return createEmptyPointcut();
        }

        ComposablePointcut cut = new ComposablePointcut(cuts.get(0));
        for (int i = 1; i < cuts.size(); i++) {
            cut.union(cuts.get(i));
        }
        return cut;
    }

    private Pointcut createPointcut(ClassFilter classFilter, MethodMatcher methodMatcher) {
        return new Pointcut() {
            @Override
            @NonNull
            public ClassFilter getClassFilter() {
                return classFilter;
            }

            @Override
            @NonNull
            public MethodMatcher getMethodMatcher() {
                return methodMatcher;
            }
        };
    }


    private Pointcut createEmptyPointcut() {
        return new Pointcut() {
            @Override
            @NonNull
            public ClassFilter getClassFilter() {
                return clazz -> false;
            }

            @Override
            @NonNull
            public MethodMatcher getMethodMatcher() {
                return MethodMatcher.TRUE;
            }
        };
    }

    private boolean inWyDeclare(Method method) {
        Class<?> cls = method.getDeclaringClass();
        String name = cls.getName();
        return matchName(name);
    }

    private boolean matchName(String name) {
        for (String path : properties.getPaths()) {
            if (name.startsWith(path)) {
                return true;
            }
        }
        return false;
    }
}

